using Autofac;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using RuYiAdmin.Net.Common.Global;
using RuYiAdmin.Net.Entity.AutoMapperConfig;
using RuYiAdmin.Net.WebApi.AppCode.ActionFilters;
using RuYiAdmin.Net.WebApi.AppCode.AuthorizationFilter;
using RuYiAdmin.Net.WebApi.AppCode.FrameworkClass;
using RuYiAdmin.Net.WebApi.AppCode.FrameworkExtensions;
using RuYiAdmin.Net.WebApi.AppCode.IoCDependencyInjection;
using Swashbuckle.AspNetCore.Filters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace RuYiAdmin.Net.WebApi
{
    public class Startup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public void ConfigureContainer(ContainerBuilder builder)
        {
            #region 注册IoC控制反转

            builder.RegisterModule<DependencyAutoInjection>();

            #endregion
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            #region 注册Swagger

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo
                {
                    Title = "RuYiAdmin.Net.WebAPI",
                    Description = "宝剑锋从磨砺出，梅花香自苦寒来。<br/><br/>如意Admin，基于.NET6平台，为开源而生。",
                    Contact = new OpenApiContact()
                    {
                        Name = "作者：不老的传说",
                        Email = "983810803@qq.com"
                    },
                    Version = "v1"
                });

                // 开启接口注释
                var xmlPath = Path.Combine(AppContext.BaseDirectory, "RuYiAdmin.Net.WebApi.xml");
                c.IncludeXmlComments(xmlPath, true);

                var xmlCommonPath = Path.Combine(AppContext.BaseDirectory, "RuYiAdmin.Net.Common.xml");
                c.IncludeXmlComments(xmlCommonPath);

                var xmlModelPath = Path.Combine(AppContext.BaseDirectory, "RuYiAdmin.Net.Entity.xml");
                c.IncludeXmlComments(xmlModelPath);

                if (GlobalContext.JwtSettings.IsEnabled)
                {
                    // header添加token
                    c.OperationFilter<SecurityRequirementsOperationFilter>();

                    c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
                    {
                        Description = "JWT授权，在下框中输入Bearer token",//注意两者之间是一个空格
                        Name = "Authorization",//jwt默认的参数名称
                        In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                        Type = SecuritySchemeType.ApiKey
                    });
                }
            });

            #endregion

            #region 注册全局跨域

            services.AddCors(options =>
            {
                options.AddPolicy("cors", builder =>
                {
                    builder.SetIsOriginAllowed(_ => true)
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials();
                });
            });

            #endregion

            #region 初始化全局配置

            InitConfiguration();

            //全局静态配置热更新
            ChangeToken.OnChange(() => Configuration.GetReloadToken(), () =>
            {
                InitConfiguration();
            });

            #endregion

            #region 注册并发策略

            services.AddQueuePolicy(options =>
            {
                //最大并发请求数
                options.MaxConcurrentRequests = GlobalContext.ConcurrencyLimiterConfig.MaxConcurrentRequests;
                //请求队列长度限制
                options.RequestQueueLimit = GlobalContext.ConcurrencyLimiterConfig.RequestQueueLimit;
            });

            #endregion

            #region 注册Jwt认证

            if (GlobalContext.JwtSettings.IsEnabled)
            {
                JwtSettings jwtSettings = new JwtSettings();
                services.Configure<JwtSettings>(Configuration.GetSection("JwtSettings"));
                Configuration.GetSection("JwtSettings").Bind(jwtSettings);

                services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                }).
                AddJwtBearer(options =>
                {
                    options.Events = new JwtBearerEvents
                    {
                        OnAuthenticationFailed = context =>
                        {
                            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                            {
                                context.Response.Headers.Add("act", "expired");
                            }
                            return Task.CompletedTask;
                        }
                    };

                    options.SaveToken = true;
                    options.RequireHttpsMetadata = false;

                    options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                    {
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = jwtSettings.Issuer,
                        ValidAudience = jwtSettings.Audience,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecurityKey)),
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        RequireExpirationTime = true,
                        ClockSkew = TimeSpan.Zero
                    };
                });
            }

            #endregion

            #region 注册过滤器

            services.AddMvc(options =>
            {
                options.Filters.Add<ActionAuthorization>(); // 添加身份验证过滤器
            });

            services.AddSingleton<LogAttribute>();// 添加审计日志过滤器
            services.AddSingleton<PermissionAttribute>(); // 添加权限验证过滤器

            #endregion

            #region 注册AutoMapper

            services.AddAutoMapper(typeof(AutoMapperProfile));

            #endregion

            #region 注册HttpContext服务

            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            #endregion

            #region 注册服务熔断与降级策略

            if (GlobalContext.PollyConfig.IsEnabled)
            {
                services.AddHttpClientPolly(GlobalContext.PollyConfig.Name);
            }

            #endregion

            #region 启用SignalR

            services.AddSignalR();

            #endregion

            #region 添加HostService

            services.AddHostedService<DistributedMessageSubscriber>();

            #endregion
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime aft)
        {
            #region POST、PUT添加Body参数

            app.Use(async (context, next) =>
            {
                if (context.Request.Method.Equals("POST", StringComparison.OrdinalIgnoreCase) ||
                context.Request.Method.Equals("PUT", StringComparison.OrdinalIgnoreCase))
                {
                    context.Request.EnableBuffering();
                    using (var reader = new StreamReader(context.Request.Body, encoding: Encoding.UTF8
                        , detectEncodingFromByteOrderMarks: false, leaveOpen: true))
                    {
                        var body = await reader.ReadToEndAsync();
                        context.Items.Add("body", body);
                        context.Request.Body.Position = 0;
                    }
                }
                await next.Invoke();
            });

            #endregion

            #region 系统全局启停事件

            aft.ApplicationStarted.Register(async () =>
            {
                Console.WriteLine("Application Started");

                //自动构建数据库
                await RuYiAdminApp.AutomaticallyBuildDatabase();

                //启动业务作业
                await RuYiAdminApp.StartScheduleJobAsync(app);

                //加载系统级别缓存
                await RuYiAdminApp.LoadSystemCache(app);
            });

            aft.ApplicationStopped.Register(() =>
            {
                Console.WriteLine("Application Stopped");
            });

            aft.ApplicationStopping.Register(async () =>
            {
                Console.WriteLine("Application Stopping");

                //清理系统缓存
                await RuYiAdminApp.ClearSystemCache(app);
            });

            #endregion

            #region 启用并发限制数中间件

            app.UseConcurrencyLimiter();

            #endregion

            #region 启用Jwt认证中间件

            if (GlobalContext.JwtSettings.IsEnabled)
            {
                //认证中间件
                app.UseAuthentication();
            }

            #endregion

            #region 启用SwaggerUI

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "RuYiAdmin.Net.WebAPI v1"));
            }
            else if (env.IsProduction() && GlobalContext.SystemConfig.SupportSwaggerOnProduction)
            {
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "RuYiAdmin.Net.WebAPI v1"));
            }

            #endregion

            #region 系统默认启用项目

            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<ChatHub>("/API/ChatHub");
                endpoints.MapControllers();
            });

            #endregion

            #region 允许跨域

            app.UseCors("cors");

            #endregion

            #region 解决Ubuntu下Nginx代理不能获取IP问题

            app.UseForwardedHeaders(new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            });

            #endregion

            #region 启用服务治理

            if (GlobalContext.ConsulConfig.IsEnabled)
            {
                app.RegisterConsul(aft);
            }

            #endregion
        }

        #region 私有方法

        /// <summary>
        /// 初始化全局静态配置
        /// </summary>
        private void InitConfiguration()
        {
            GlobalContext.DBConfig = Configuration.GetSection("DBConfig").Get<DBConfig>();
            GlobalContext.SystemConfig = Configuration.GetSection("SystemConfig").Get<SystemConfig>();
            GlobalContext.Configuration = Configuration;
            GlobalContext.RedisConfig = Configuration.GetSection("RedisConfig").Get<RedisConfig>();
            GlobalContext.ActiveMQConfig = Configuration.GetSection("ActiveMQConfig").Get<ActiveMQConfig>();
            GlobalContext.JwtSettings = Configuration.GetSection("JwtSettings").Get<JwtSettings>();
            GlobalContext.LogConfig = Configuration.GetSection("LogConfig").Get<LogConfig>();
            GlobalContext.MailConfig = Configuration.GetSection("MailConfig").Get<MailConfig>();
            GlobalContext.DirectoryConfig = Configuration.GetSection("DirectoryConfig").Get<DirectoryConfig>();
            GlobalContext.QuartzConfig = Configuration.GetSection("QuartzConfig").Get<QuartzConfig>();
            GlobalContext.RestSharpConfig = Configuration.GetSection("RestSharpConfig").Get<RestSharpConfig>();
            GlobalContext.PollyConfig = Configuration.GetSection("PollyConfig").Get<PollyConfig>();
            GlobalContext.ConsulConfig = Configuration.GetSection("ConsulConfig").Get<ConsulConfig>();
            GlobalContext.ConcurrencyLimiterConfig = Configuration.GetSection("ConcurrencyLimiterConfig").Get<ConcurrencyLimiterConfig>();
            GlobalContext.SmartThreadPoolConfig = Configuration.GetSection("SmartThreadPoolConfig").Get<SmartThreadPoolConfig>();
            GlobalContext.SystemCacheConfig = Configuration.GetSection("SystemCacheConfig").Get<SystemCacheConfig>();
            GlobalContext.MOMConfig = Configuration.GetSection("MOMConfig").Get<MOMConfig>();
            GlobalContext.RabbitMQConfig = Configuration.GetSection("RabbitMQConfig").Get<RabbitMQConfig>();
        }

        #endregion
    }
}
